package com.insight.service;

import com.insight.common.enums.ExportFormat;
import com.insight.common.enums.ExportStatus;
import com.insight.entity.DataSource;
import com.insight.entity.DictionaryExport;
import com.insight.entity.MetadataTable;
import com.insight.export.generator.ExportGenerator;
import com.insight.repository.DictionaryExportRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * T095: 数据字典导出服务
 * 管理导出任务、文件归档和过期清理
 *
 * @author Insight Team
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ExportService {

    private final DictionaryExportRepository exportRepository;
    private final MinIOService minioService;
    private final Map<ExportFormat, ExportGenerator> generators;

    @Value("${insight.export.temp-directory:./temp/exports}")
    private String tempDirectory;

    @Value("${insight.export.file-expiry-days:7}")
    private int fileExpiryDays;

    @Value("${minio.bucket.exports}")
    private String exportsBucket;

    private static final DateTimeFormatter TIMESTAMP_FORMAT =
            DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");

    /**
     * 导出数据字典
     *
     * @param dataSource 数据源
     * @param tables     表列表
     * @param format     导出格式
     * @return 导出记录
     */
    @Transactional
    public DictionaryExport exportDictionary(DataSource dataSource, List<MetadataTable> tables,
                                             ExportFormat format) {
        log.info("开始导出数据字典: dataSource={}, format={}, tableCount={}",
                dataSource.getName(), format, tables.size());

        // 验证生成器
        ExportGenerator generator = generators.get(format);
        if (generator == null) {
            throw new IllegalArgumentException("不支持的导出格式: " + format);
        }

        // 创建导出记录
        DictionaryExport export = new DictionaryExport();
        export.setDataSource(dataSource);
        export.setFormat(format);
        export.setStatus(ExportStatus.SUCCESS);
        export.setIsArchived(false);

        try {
            // 确保输出目录存在
            Path outputDir = Paths.get(tempDirectory);
            if (!Files.exists(outputDir)) {
                Files.createDirectories(outputDir);
            }

            // 生成文件
            Path generatedFile = generator.generate(dataSource, tables, outputDir);

            // 设置文件信息
            export.setFileName(generatedFile.getFileName().toString());
            export.setLocalPath(generatedFile.toString());
            export.setFileSize(Files.size(generatedFile));
            export.setExpiresAt(LocalDateTime.now().plusDays(fileExpiryDays));

            log.info("数据字典导出成功: file={}, size={} bytes",
                    export.getFileName(), export.getFileSize());

        } catch (Exception e) {
            log.error("数据字典导出失败: dataSource={}, format={}",
                    dataSource.getName(), format, e);
            export.setStatus(ExportStatus.FAILED);
            export.setLocalPath(null);
            export.setFileSize(null);
            export.setExpiresAt(null);
        }

        // 保存导出记录
        return exportRepository.save(export);
    }

    /**
     * 归档到MinIO
     *
     * @param export 导出记录
     * @return 更新后的导出记录
     * @throws IOException MinIO操作异常
     */
    @Transactional
    public DictionaryExport archiveToMinIO(DictionaryExport export) throws IOException {
        log.info("开始归档到MinIO: exportId={}, fileName={}",
                export.getId(), export.getFileName());

        // 验证状态
        if (export.getStatus() != ExportStatus.SUCCESS) {
            throw new IllegalStateException("只能归档状态为SUCCESS的导出记录");
        }

        // 验证本地文件存在
        if (export.getLocalPath() == null) {
            throw new IOException("本地文件路径为空");
        }

        Path localFile = Paths.get(export.getLocalPath());
        if (!Files.exists(localFile)) {
            throw new IOException("本地文件不存在: " + localFile);
        }

        try {
            // 生成MinIO对象路径: exports/yyyy/MM/filename
            LocalDateTime now = LocalDateTime.now();
            String objectPath = String.format("exports/%04d/%02d/%s",
                    now.getYear(), now.getMonthValue(), export.getFileName());

            // 上传到MinIO
            String minioPath = minioService.uploadFile(exportsBucket, objectPath, localFile);

            // 更新导出记录
            export.setMinioPath(minioPath);
            export.setIsArchived(true);

            // 删除本地文件
            Files.deleteIfExists(localFile);
            export.setLocalPath(null);

            log.info("归档到MinIO成功: exportId={}, minioPath={}",
                    export.getId(), minioPath);

            return exportRepository.save(export);

        } catch (Exception e) {
            log.error("归档到MinIO失败: exportId={}", export.getId(), e);
            throw new IOException("归档失败: " + e.getMessage(), e);
        }
    }

    /**
     * 清理过期文件
     *
     * @return 清理的文件数量
     */
    @Transactional
    public int cleanupExpiredFiles() {
        log.info("开始清理过期文件");

        List<DictionaryExport> expiredExports = exportRepository.findExpiredExports(LocalDateTime.now());
        int cleanedCount = 0;

        for (DictionaryExport export : expiredExports) {
            try {
                // 删除本地文件
                if (export.getLocalPath() != null) {
                    Path localFile = Paths.get(export.getLocalPath());
                    Files.deleteIfExists(localFile);
                    log.debug("删除本地过期文件: {}", localFile);
                }

                // 删除MinIO文件
                if (export.getIsArchived() && export.getMinioPath() != null) {
                    minioService.deleteObject(exportsBucket, export.getMinioPath());
                    log.debug("删除MinIO过期文件: {}", export.getMinioPath());
                }

                // 更新状态为EXPIRED
                export.setStatus(ExportStatus.EXPIRED);
                export.setLocalPath(null);
                export.setMinioPath(null);
                exportRepository.save(export);

                cleanedCount++;

            } catch (Exception e) {
                log.warn("清理过期文件失败: exportId={}", export.getId(), e);
                // 继续处理其他文件
            }
        }

        log.info("过期文件清理完成: 处理{}个, 成功{}个", expiredExports.size(), cleanedCount);
        return cleanedCount;
    }

    /**
     * 根据ID查询导出记录
     *
     * @param id 导出ID
     * @return 导出记录
     */
    public Optional<DictionaryExport> getExportById(Long id) {
        return exportRepository.findById(id);
    }

    /**
     * 根据数据源查询所有导出
     *
     * @param datasourceId 数据源ID
     * @return 导出记录列表
     */
    public List<DictionaryExport> getExportsByDataSource(Long datasourceId) {
        if (datasourceId == null) {
            return exportRepository.findAll();
        }
        return exportRepository.findByDataSourceId(datasourceId);
    }

    /**
     * 获取数据源指定格式的最新导出
     *
     * @param datasourceId 数据源ID
     * @param format       导出格式
     * @return 最新的导出记录
     */
    public Optional<DictionaryExport> getLatestExport(Long datasourceId, ExportFormat format) {
        return exportRepository.findFirstByDataSourceIdAndFormatOrderByCreatedAtDesc(
                datasourceId, format);
    }

    /**
     * 获取待归档的导出列表
     *
     * @return 待归档的导出记录列表
     */
    public List<DictionaryExport> getPendingArchiveExports() {
        return exportRepository.findPendingArchiveExports();
    }

    /**
     * 删除导出记录及相关文件
     *
     * @param id 导出ID
     */
    @Transactional
    public void deleteExport(Long id) {
        log.info("删除导出记录: id={}", id);

        DictionaryExport export = exportRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("导出记录不存在: " + id));

        try {
            // 删除本地文件
            if (export.getLocalPath() != null) {
                Path localFile = Paths.get(export.getLocalPath());
                Files.deleteIfExists(localFile);
                log.debug("删除本地文件: {}", localFile);
            }

            // 删除MinIO文件
            if (export.getIsArchived() && export.getMinioPath() != null) {
                minioService.deleteObject(exportsBucket, export.getMinioPath());
                log.debug("删除MinIO文件: {}", export.getMinioPath());
            }

        } catch (Exception e) {
            log.warn("删除文件失败: exportId={}", id, e);
            // 继续删除数据库记录
        }

        // 删除数据库记录
        exportRepository.deleteById(id);
        log.info("导出记录删除完成: id={}", id);
    }

    /**
     * 生成下载URL
     *
     * @param export 导出记录
     * @return 下载URL
     * @throws IOException MinIO操作异常
     */
    public String generateDownloadUrl(DictionaryExport export) throws IOException {
        // 验证状态
        if (export.getStatus() != ExportStatus.SUCCESS) {
            throw new IllegalStateException("只有SUCCESS状态的导出才能下载");
        }

        // MinIO已归档文件 - 生成预签名URL
        if (export.getIsArchived() && export.getMinioPath() != null) {
            log.debug("生成MinIO预签名URL: exportId={}", export.getId());
            return minioService.generatePresignedDownloadUrl(exportsBucket, export.getMinioPath());
        }

        // 本地文件 - 返回API路径
        if (export.getLocalPath() != null) {
            log.debug("返回本地文件API路径: exportId={}", export.getId());
            return String.format("/api/exports/%d/download", export.getId());
        }

        throw new IllegalStateException("无可用的下载文件");
    }

    /**
     * 统计数据源的导出数量
     *
     * @param datasourceId 数据源ID
     * @return 导出数量
     */
    public long countByDataSource(Long datasourceId) {
        return exportRepository.countByDataSourceId(datasourceId);
    }

    /**
     * 统计指定状态的导出数量
     *
     * @param status 导出状态
     * @return 导出数量
     */
    public long countByStatus(ExportStatus status) {
        return exportRepository.countByStatus(status);
    }
}
